home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power CD-ROM!! 8
/
Power CD-ROM 8.iso
/
dos
/
shred
/
shred.asm
< prev
next >
Wrap
Assembly Source File
|
1994-09-27
|
38KB
|
770 lines
_TEXT SEGMENT PUBLIC 'CODE'
ASSUME CS:_TEXT,DS:_TEXT,ES:_TEXT,SS:_TEXT
ORG 100H
START: JMP MAIN
; DATA AREA
; ---------
SIGNATURE DB CR,SPACE,SPACE,SPACE,CR,LF
COPYRIGHT DB "SHRED 1.0 (c) 1995 Michael J. Mefford",CR,LF
First_Rights DB "First Published in PC Magazine, "
DB "January 10, 1995",CR,LF,LF
DB "Syntax: SHRED [d:] [filespec] [/S] [/F | /D] [/C]",CR,LF,LF
DB "Insures confidentiality by writing zeros over sensitive data",CR,LF
DB "/S Subdirectories.",CR,LF
DB "/F Free space on disk shredded.",CR,LF
DB "/D Delete only; no shredding.",CR,LF
DB "/C Confirmation before shredding suppressed."
DOUBLE_SPACE DB LF
CR_LF DB CR,LF,0,CTRL_Z
CR EQU 13
LF EQU 10
CTRL_Z EQU 26
SPACE EQU 32
BOX EQU 254
Y_SCAN EQU 15H
N_SCAN EQU 31H
ESC_SCAN EQU 01H
CTRL_C EQU 03H
ENTER_SCAN EQU 1CH
DIRECTORY EQU 10H
MATCHING STRUC
RESERVED DB 21 DUP (?)
ATTRIBUTE DB ?
FILE_TIME DW ?
FILE_DATE DW ?
SIZE_LOW DW ?
SIZE_HIGH DW ?
FILE_NAME DB 13 DUP (?)
MATCHING ENDS
BYTES_PER_CLUSTER DW ?
AVAILABLE_HIGH DW ?
AVAILABLE_LOW DW ?
ZERO_SEG DW ?
CTRL_BREAK DB ?
CURRENT_DISK DB ?
FILESPEC DW ?
DIRECTORY_LINE DW ?
OPEN_FORMAT DW 3C00H ;Create file.
SWITCH_FLAG DB 0
SUBDIRS EQU 01H
FREE EQU 02H
DELETE_ONLY EQU 04H
CONFIRM EQU 08H
DISK EQU 10H
NOT_FOUND DB CR,LF,"File not found",0
NOT_ENOUGH DB "Not enough memory",0
DIRECTORY_OF DB "Directory of ",0
SHREDDING DB "Shredding ",0
DELETING DB "Deleting ",0
FREE_SPACE DB "All the FREE space of drive ",0
WILL_BE DB "will be ",0
SHREDDED DB "shredded. ",0
DELETED DB "deleted. ",0
WORKING DB "... working",0
WARNING DB "WARNING: ",0
CONTINUE DB "Continue? (Y/N or Esc to Quit) ",0
TO_CONFIRM DB CR,LF,"(Press ",17,217," to confirm, Esc to cancel) ",0
ABORTED DB CR,LF,LF,"Aborted",CR,LF,0
TEMP DB "SHRED.TMP",0
STAR_DOT_STAR DB "*.*",0
DOT_DOT DB "..",0
PROTECTED_FILE DB "is a read-only and can't be shredded. Press any key.",0
; CODE AREA
; ---------
MAIN PROC NEAR
CLD ;String moves forward.
MOV AX,500H
INT 10H ;Select active page zero.
MOV AH,0FH
INT 10H ;Get current video mode.
CMP AL,7 ;If it's MONO,
JZ CK_CURSOR
CMP AL,2 ; CO80,
JZ CK_CURSOR
CMP AL,3 ; or BW80, then OK mode.
JZ CK_CURSOR
MOV AX,3 ;Else, change to CO80.
INT 10H
CK_CURSOR: CALL GET_CURSOR ;Read cursor position.
CMP DH,1 ;If DOS prompt already
JZ SIGN_ON ; at top, skip move.
MOV AH,8 ;Else, read attr/char
INT 10H ; at cursor position.
MOV BH,AH ;Move DOS command line to top
XOR CX,CX ; of screen by scrolling active
MOV DL,80 - 1 ; page with current attribute.
MOV AL,DH
DEC AL
MOV AH,6
INT 10H
SIGN_ON: MOV DX,100H ;Set cursor to line 2 (logical 1)
CALL SET_CURSOR
MOV SI,OFFSET SIGNATURE ;And display copyright
CALL PRINT_STRING ; and syntax.
MOV BX,OFFSET ZEROS ;Allocate memory for code, stack
ADD BX,15 ; and 64K for a segment of zeros.
MOV CL,4
SHR BX,CL
MOV AX,DS
ADD AX,BX
MOV ZERO_SEG,AX ;Save address of segment of zeros
ADD BX,4096
MOV AH,4AH
INT 21H
JNC SETUP_STACK
MOV SI,OFFSET NOT_ENOUGH ;Error msg if not enough memory.
JMP MSG_EXIT
SETUP_STACK: MOV SP,OFFSET STACK_PTR ;Move stack pointer down.
MOV DX,OFFSET DTA ;To preserve command line, change
MOV AH,1AH ; DTA from PSP to end of code.
INT 21H
MOV ES,ZERO_SEG ;Fill extra segment with zeros.
XOR DI,DI
XOR AX,AX
MOV CX,32768
REP STOSW
PUSH CS
POP ES
MOV AX,3300H ;Retrieve current Ctrl
INT 21H ; break state and store.
MOV CTRL_BREAK,DL
XOR DL,DL
MOV AX,3301H ;Turn Ctrl break off.
INT 21H
MOV AH,19H ;Get current drive
INT 21H
MOV CURRENT_DISK,AL ; and save.
;----------------------------------------------;
PARSE: MOV SI,81H ;Point to command line parameter.
FIND_SWITCH: LODSB ;Get a byte.
CMP AL,CR ;Carriage return marks end.
JZ FIND_FILESPEC ;If end, done here.
CMP AL,"/" ;Is it a switch character?
JNZ FIND_SWITCH ;If no, next byte.
LODSB ;Else, retrieve switch.
AND AL,5FH ;Else, capitalize.
CMP AL,"S" ;Is it subdirectories?
JNZ CK_FREE ;If no, check free switch.
OR SWITCH_FLAG,SUBDIRS ;Else, flag to do subdirectories.
CK_FREE: CMP AL,"F" ;Is it disk free switch?
JNZ CK_DELETE ;If no, check delete switch.
OR SWITCH_FLAG,FREE ;Else, flag to zero free space.
CK_DELETE: CMP AL,"D" ;Is it delete-only switch?
JNZ CK_CONFIRM ;If no, check confirm switch.
OR SWITCH_FLAG,DELETE_ONLY ;Else, flag to delete-only.
CK_CONFIRM: CMP AL,"C" ;Is it suppress confirm switch?
JNZ BAD_SWITCH ;If no, exit.
OR SWITCH_FLAG,CONFIRM ;Else, flag to suppress prompts.
JMP FIND_SWITCH ;Next switch.
BAD_SWITCH: MOV AL,1
JMP RESTORE
;------------------------------------------------------------------;
; Parse drive and/or path delimiters. Convert filespec to ASCIIZ. ;
; If drive delimiter found (:), change to requested drive. ;
;------------------------------------------------------------------;
FIND_FILESPEC: MOV SI,81H ;Point to command line parameter.
PARSE_LEADING: LODSB ;Get a byte.
CMP AL,SPACE ;Is it a space char or below?
JA LEADING_END ;If no, done here.
CMP AL,CR ;Is it carriage return?
JNZ PARSE_LEADING ;If no, get next byte.
LEADING_END: DEC SI ;Adjust pointer to string start.
MOV BP,SI ;Save start of filespec.
MOV BX,SI ;Use BX as filename start pointer
FIND_END: LODSB ;Get a byte.
CMP AL,":" ;Is it a drive delimiter?
JNZ CK_SLASH ;If no, check path delimiter.
MOV DL,[SI-2] ;Else, retrieve drive specifier.
AND DL,5FH ;Capitalize.
SUB DL,"A" ;Convert to DOS format.
MOV AH,0EH ;Change drive.
INT 21H
MOV BX,SI ;Save as filename start.
JMP FIND_END ;Continue parsing.
CK_SLASH: CMP AL,"\" ;Is it a path delimiter?
JNZ CK_DELIMITER ;If no, check switch character.
MOV BX,SI ;Else, save as filename start.
CK_DELIMITER: CMP AL,"/" ;Is it a switch delimiter?
JZ FOUND_END ;If yes, end of filespec.
CMP AL,SPACE ;Is it above space character?
JA FIND_END ;If yes, continue until find end.
FOUND_END: DEC SI ;Adjust.
PUSH SI ;Save filespec end.
PUSH BX ;Save filespec start.
MOV BYTE PTR [SI],0 ;Convert to ASCIIZ.
CMP BP,SI ;Any filespec?
JZ NO_SPEC ;If no, treat as /F.
CMP BYTE PTR [SI - 1],":" ;Drive specifier only?
JNZ GET_CURRENT ;If no, skip
NO_SPEC: OR SWITCH_FLAG,DISK
GET_CURRENT: MOV SI,OFFSET CURRENT_DIR ;Retrieve current directory.
CALL GET_DIR
MOV AH,19H ;Retrieve current disk.
INT 21H
ADD AL,"A" ;Convert to ASCII.
MOV BYTE PTR WORKING_DIR,AL ;Save drive
MOV BYTE PTR WORKING_DIR + 1,":" ; and colon delimiter.
CALL GET_AVAILABLE ;Get disk free space.
TEST SWITCH_FLAG,FREE OR DISK ;Was there /F or no filespec
JZ FIND_PATH ; or drive-only?; If no, skip.
TEST SWITCH_FLAG,DELETE_ONLY ;Was there /D delete only?
JNZ FIND_PATH ;If yes, skip.
CALL SHRED_FREE ;Else, shred free disk space.
MOV SI,OFFSET DOUBLE_SPACE ;Add double space
CALL PRINT_STRING ; between outputs.
;-------------------------------------------------;
FIND_PATH: POP BX ;Retrieve filename start.
POP SI ;Retrieve filespec end.
TEST SWITCH_FLAG,DISK ;Was filespec drive-only?
JZ CK_ROOT ;If no, continue.
JMP GOOD_EXIT ;Else, done.
CK_ROOT: MOV CX,1 ;CX=1:"\"not=root; CX=0:"\"=root.
CMP BYTE PTR [BX - 1],"\" ;Filespec start path delimiter?
JNZ CK_PATH ;If no, not root.
CMP BYTE PTR [BX - 2],":" ;Else, is it prefaced with colon?
JZ ROOT ;If yes, then root.
CMP BYTE PTR [BX - 2],SPACE ;Is it prefaced with white space?
JA CK_TRAILING ;If no, then trailing slash?
ROOT: DEC CX ;Else, root; CX=0 for root flag.
JMP SHORT CK_PATH ;Change default path.
CK_TRAILING: CMP BX,SI ;Filename start = filespec end?
JNZ CK_PATH ;If no, not trailing slash.
MOV BYTE PTR [BX - 1],0 ;Else, zero out trailing slash
MOV BX,OFFSET STAR_DOT_STAR ; and use global filespec.
CK_PATH: MOV DX,BP ;See if filespec is a path
CALL CHANGE_DIR ; by changing directory.
JC CK_FILESPEC ;If failed, remove filename.
MOV BX,OFFSET STAR_DOT_STAR ;Else, use global for filename.
JMP SHORT GOT_FILESPEC ;Done here.
CK_FILESPEC: JCXZ SAVE_DELIMIT ;Is path root?; If yes leave "\".
DEC BX ;Else, point to slash.
SAVE_DELIMIT: PUSH [BX] ;Preserve filename start.
MOV BYTE PTR [BX],0 ;Temp ASCIIZ twixt path and name.
CALL CHANGE_DIR ;Change directory.
POP [BX] ;Restore first byte of filename.
JCXZ GOT_FILESPEC ;If root, done here.
INC BX ;Else, readjust filename pointer.
GOT_FILESPEC: MOV FILESPEC,BX ;Save filename.
;----------------------------------------------;
MOV BP,OFFSET TREE_LEVEL ;Initialize directory tree
MOV DI,BP ; level array to level one.
MOV AX,0101H
MOV CX,50
REP STOSW
CALL GET_CURSOR ;Get relative line for
MOV DIRECTORY_LINE,DX ; directory display.
MOV OPEN_FORMAT,3D01H ;Open file for writing.
NEXT_LEVEL: MOV SI,OFFSET WORKING_DIR + 2 ;Display current directory.
CALL GET_DIR
MOV SI,OFFSET DIRECTORY_OF
CALL PRINT_STRING
MOV SI,OFFSET WORKING_DIR
CALL PRINT_STRING
FIND_FIRST: MOV DX,FILESPEC ;Find first matching normal
XOR CX,CX ; file to filespec.
MOV AH,4EH
INT 21H
JNC CK_READ_ONLY
MOV SI,OFFSET NOT_FOUND ;If failed, display message.
CALL PRINT_STRING
JMP SHORT CK_SUBDIRS ;Check /Subdir switch.
FIND_NEXT: MOV AH,4FH ;Find next matching file.
INT 21H
JC CK_SUBDIRS ;If failed, check /Subdir switch.
CK_READ_ONLY: TEST BYTE PTR DTA.ATTRIBUTE, 1
JZ CK_WARNING
MOV DX,DIRECTORY_LINE
INC DH
CALL BLANK_LINE
CALL DISPLAY_NAME
MOV SI,OFFSET PROTECTED_FILE
CALL PRINT_STRING
CALL CLEAR_KEY
CALL GET_KEY
JMP FIND_NEXT
CK_WARNING: MOV DX,DIRECTORY_LINE ;Move cursor to line below
INC DH ; directory.
CALL BLANK_LINE ;Blank out last display.
TEST SWITCH_FLAG,CONFIRM ;Did user suppress prompts?
JNZ NO_WARNING ;If yes, no warning.
MOV SI,OFFSET WARNING ;Else, display warning message.
CALL PRINT_STRING
CALL DISPLAY_NAME ;Display complete stats of file.
MOV SI,OFFSET WILL_BE
CALL PRINT_STRING ;Display message.
TEST SWITCH_FLAG,DELETE_ONLY
JZ MESSAGE1
MOV SI,OFFSET DELETED
MESSAGE1: CALL PRINT_STRING
CALL PROMPT ;Ask user if should continue.
JC FIND_NEXT ;If no, find next matching.
CALL SHRED_FILE ;Else, shred the file.
JMP FIND_NEXT ;Find next matching file.
NO_WARNING: CALL CK_KEY ;Let user abort the /C suppress
JNZ ERROR_EXIT ; confirm mode with any keypress.
MOV SI,OFFSET SHREDDING ;Display message
TEST SWITCH_FLAG,DELETE_ONLY
JZ MESSAGE2
MOV SI,OFFSET DELETING
MESSAGE2: CALL PRINT_STRING
CALL DISPLAY_NAME ; and file stats.
CALL SHRED_FILE ;Shred the file.
JMP FIND_NEXT ;Next matching.
;----------------------------------------------;
CK_SUBDIRS: TEST SWITCH_FLAG,SUBDIRS ;Was there a /Subdir switch?
JNZ FIRST_DIR ;If yes, continue.
JMP SHORT GOOD_EXIT ;Else, exit.
PARENT_DIR: CMP BP,OFFSET TREE_LEVEL ;Are we at tree level where we
JZ GOOD_EXIT ; started? If yes, done here.
MOV BYTE PTR [BP],1 ;Else, first dir of this level.
DEC BP ;Go down a level.
MOV DX,OFFSET DOT_DOT ;Change to parent directory.
CALL CHANGE_DIR
FIRST_DIR: XOR BL,BL ;BL = directory number.
MOV DX,OFFSET STAR_DOT_STAR ;Global search.
MOV CX,DIRECTORY ;Ask for directories.
MOV AH,4EH
INT 21H
JC PARENT_DIR ;If no match, down a level.
JMP SHORT CK_DIR ;Else, see if it's a directory.
NEXT_DIR: MOV AH,4FH ;Get all subdirectories in
INT 21H ; current directory then
JC PARENT_DIR ; to parent.
CK_DIR: CMP BYTE PTR DTA.ATTRIBUTE,DIRECTORY ;Is it a directory?
JNZ NEXT_DIR ;If no, next matching.
CMP BYTE PTR DTA.FILE_NAME,'.' ;is it a dot directory?
JZ NEXT_DIR ;If no, next matching.
INC BL ;Increment position in directory.
CMP BL,[BP] ;Continue until new directory.
JNZ NEXT_DIR
INC BYTE PTR DS:[BP] ;Update this level dir count.
MOV DX,OFFSET DTA.FILE_NAME
CALL CHANGE_DIR ;Change the directory.
INC BP ;Up a level.
MOV DX,DIRECTORY_LINE ;Blank out last display.
CALL BLANK_LINE
JMP NEXT_LEVEL ;Get all files in new directory.
;----------------------------------------------;
ERROR_EXIT: MOV SI,OFFSET ABORTED ;Display abort message.
MSG_EXIT: CALL PRINT_STRING
MOV AL,1 ;ERRORLEVEL = 1.
JMP SHORT RESTORE ;Restore defaults.
GOOD_EXIT: XOR AL,AL ;ERRORLEVEL = 0.
RESTORE: PUSH AX ;Save ERRORLEVEL.
MOV DL,CTRL_BREAK ;Restore break state.
MOV AX,3301H
INT 21H
MOV DX,OFFSET CURRENT_DIR ;Restore default directory.
CALL CHANGE_DIR
MOV DL,CURRENT_DISK ;Restore default drive.
MOV AH,0EH
INT 21H
POP AX ;Retrieve ERRORLEVEL.
EXIT: MOV AH,4CH
INT 21H ;Terminate.
MAIN ENDP
;***************;
;* SUBROUTINES *;
;***************;
SHRED_FREE: PUSH BP ;Preserve base pointer.
MOV DX,OFFSET TEMP ;See if temporary file exits.
MOV CX,1FH ;Use all attributes.
NEXT_TEMP: MOV AH,4EH
INT 21H
JC GOT_TEMP ;If it doesn't, OK.
MOV BX,DX
INC BYTE PTR [BX] ;Else, change name.
JNZ NEXT_TEMP ;Give up after several name
JMP ERROR_EXIT ; changes and abort.
GOT_TEMP: MOV BP,DX ;Point to temporary filename.
TEST SWITCH_FLAG,CONFIRM ;Do we need user permission to
JNZ NO_PROMPT ; continue? If no, no prompt.
CALL FREE_MSG ;Else, display message.
MOV SI,OFFSET WILL_BE
CALL PRINT_STRING
MOV SI,OFFSET WILL_BE
CALL PRINT_STRING
CALL PRINT_STRING
MOV SI,OFFSET CR_LF
CALL PRINT_STRING
CALL PROMPT ;Ask user if should continue.
JC FREE_END ;If no, done here.
JMP SHORT DO_FREE ;Else, shred the disk free space.
NO_PROMPT: CALL CK_KEY ;Let user abort the /C suppress
JNZ ERROR_EXIT ; confirm mode with any keypress.
MOV SI,OFFSET SHREDDING ;Display message.
CALL PRINT_STRING
CALL FREE_MSG
MOV SI,OFFSET WORKING ;Display "... working" message.
CALL PRINT_STRING
DO_FREE: MOV SI,AVAILABLE_HIGH ;Create a temporary file and
MOV DI,AVAILABLE_LOW ; write zeros bytes equal to
CALL WRITE_ZEROS ; the amount of free space.
FREE_END: POP BP ;Restore base pointer.
RET
;----------------------------------------------;
FREE_MSG: MOV SI,OFFSET FREE_SPACE ;Display free space message.
CALL PRINT_STRING
MOV AL,BYTE PTR WORKING_DIR ;Display the working drive.
MOV AH,0EH
INT 10H
MOV AX,0EH SHL 8 + SPACE
INT 10H
RET
;----------------------------------------------;
SHRED_FILE: PUSH BP ;Preserve base pointer.
MOV BP,OFFSET DTA.FILE_NAME ;Point to filename.
MOV SI,DTA.SIZE_HIGH ;Retrieve size of file.
MOV DI,DTA.SIZE_LOW
MOV DX,SI
MOV AX,DI
MOV CX,BYTES_PER_CLUSTER ;Round up file size to nearest
DIV CX ; whole cluster by dividing by
SUB CX,DX ; bytes per cluster and
ADD DI,CX ; adding remainder to size.
ADC SI,0
CALL WRITE_ZEROS ;Write zeros over the file.
POP BP ;Restore base pointer.
RET
;---------------------------------------------------------------;
; INPUT: BP -> filename; SI:DI = number of zero bytes to write. ;
;---------------------------------------------------------------;
WRITE_ZEROS: TEST SWITCH_FLAG,DELETE_ONLY ;Is the delete-only flag set?
JNZ DELETE_FILE ;If yes, don't write zeros.
MOV DX,BP ;Filename pointer.
XOR CX,CX ;Normal attribute.
MOV AX,OPEN_FORMAT ;Create new or open existing.
INT 21H
JC WRITE_FAIL ;If failed, exit.
MOV BX,AX ;Else, save filehandle.
NEXT_WRITE: MOV CX,DI ;Retrieve low half of byte count.
OR SI,SI ;Is high half zero?
JZ SUBTRACT ;If yes, continue.
MOV CX,0FFFFH ;Else, write full word (65535).
SUBTRACT: SUB DI,CX ;Subtract from running count.
SBB SI,0 ;If borrow, adjust high half.
JCXZ CLOSE_FILE ;If zero bytes, done here.
PUSH DS ;Preserve data segment.
MOV DS,ZERO_SEG ;Use segment of zeros.
XOR DX,DX ;Offset of zero.
MOV AH,40H ;Write to file.
INT 21H
POP DS ;Restore data segment.
JC WRITE_FAIL ;If failed, exit.
CMP AX,CX ;Did we write what we asked?
JNZ WRITE_FAIL ;If no, failed; exit.
JMP NEXT_WRITE ;Else, write next chunk.
CLOSE_FILE: MOV AH,3EH ;Close file.
INT 21H
JC WRITE_FAIL ;If failed, exit.
DELETE_FILE: MOV DX,BP
MOV AH,41H ;Delete file.
INT 21H
JC WRITE_FAIL ;If failed, exit.
ZEROS_END: RET
WRITE_FAIL: MOV DX,OFFSET NOT_FOUND ;With all failures, display
CALL PRINT_STRING ; generic "file not found"
JMP ERROR_EXIT ; message and abort.
;----------------------------------------------------------------------;
; OUTPUT: CY = 0: Yes; CY = 1: No; If Esc or ^C, go directly to exit. ;
;----------------------------------------------------------------------;
PROMPT: MOV SI,OFFSET CONTINUE ;Display message.
CALL PRINT_STRING
CALL CLEAR_KEY ;Clear keyboard buffer.
GET_INPUT: CALL GET_KEY ;Now get a keystroke.
CMP AH,Y_SCAN ;Was it "Y"?
JNZ CK_NO
MOV AH,0EH ;If yes, display "Y" and
INT 10H
CALL PRINT_STRING ; ask for confirmation.
GET_CONFIRM: CALL GET_KEY
CMP AH,ESC_SCAN
STC
JZ PROMPT_END
CMP AH,ENTER_SCAN
JNZ GET_CONFIRM
MOV SI,OFFSET WORKING
CALL PRINT_STRING
CLC
JMP SHORT PROMPT_END
CK_NO: CMP AH,N_SCAN ;Was it "N"?
STC
JZ PROMPT_END
CMP AH,ESC_SCAN ;Was it Esc?
JZ BREAK
OR AH,AH ;Was it Ctrl Break?
JZ BREAK
CMP AL,CTRL_C ;Was it Ctrl C?
JNZ GET_INPUT ;If Esc, Ctrl Break or Ctrl C,
BREAK: JMP ERROR_EXIT ; immediate exit.
PROMPT_END: RET ;Else, return keystroke.
;----------------------------------------------;
GET_AVAILABLE: XOR DL,DL ;Default drive.
MOV AH,36H ;Get disk free space.
INT 21H
MUL CX ;Bytes/sector * sectors/cluster
MOV BYTES_PER_CLUSTER,AX ; equals bytes/cluster.
MUL BX ;Avail clusters * bytes/cluster
MOV AVAILABLE_HIGH,DX ; equals available bytes.
MOV AVAILABLE_LOW,AX
RET
;----------------------------------------------;
DISPLAY_NAME: MOV SI,OFFSET DTA.FILE_NAME ;Point to filename.
MOV DI,OFFSET RESULTS ;And storage area.
MOV AX,SPACE SHL 8 + SPACE ;Initiate storage with spaces.
MOV CX,20
REP STOSW
MOV DI,OFFSET RESULTS ;Point to start of storage again.
MOV CX,12 ;Store 12 bytes of filename.
NEXT_STORE: LODSB ;Get a byte.
OR AL,AL ;End of filename?
JZ END_STORE ;If yes, finish with blanks.
CMP AL,"." ;Is it the period?
JNZ STORE_BYTE ;If no, store.
SUB CX,3 ;Else store 3 spaces.
MOV AL,SPACE
REP STOSB
ADD CX,3
JMP SHORT NEXT_STORE ;Get next byte.
STORE_BYTE: STOSB ;Store byte.
LOOP NEXT_STORE ;Get next byte.
END_STORE: MOV AL,SPACE ;Pad balance with spaces.
REP STOSB
PUSH DI ;Save pointer.
ADD DI,8 ;Move to end of bytes field.
MOV DX,DTA.SIZE_LOW ;Retrieve high and low words
MOV AX,DTA.SIZE_HIGH ; of size in bytes.
MOV BX,10 ;Convert to decimal.
STD ;Reverse direction.
NEXT_SIZE: MOV CX,DX ;Low word in CX.
XOR DX,DX ;Zero in high half.
DIV BX ;Convert to decimal.
XCHG AX,CX ;Retrieve low word.
DIV BX
XCHG AX,DX ;Retrieve remainder.
ADD AL,"0" ;Convert to ASCII.
STOSB ;Store it.
MOV AX,CX ;Are we done?
OR CX,DX
JNZ NEXT_SIZE ;If no, divide again.
CLD ;Back to forward direction.
POP DI ;Retrieve pointer.
ADD DI,11 ;Move to date field.
DATE: MOV DX,DTA.FILE_DATE ;Retrieve date.
MOV AX,DX
MOV CL,5 ;Shift to lowest bits.
SHR AX,CL
AND AX,1111B ;Mask off all but month.
MOV CL,0FFH ;Flag as no leading zeros.
MOV CH,"-" ;Delimiting character.
CALL STORE_WORD ;Store it.
MOV AX,DX ;Retrieve date.
AND AX,11111B ;Mask off all but day.
XOR CL,CL ;Flag include leading zeros.
MOV CH,"-"
CALL STORE_WORD ;Store it.
MOV AX,DX ;Retrieve date for last time.
MOV CL,9
SHR AX,CL ;Mask off all but year.
ADD AX,80 ;Adjust to ASCII.
CMP AX,100 ;Past year 2000?
JB DISPLAY_DATE ;If no, display. Else, adjust for
SUB AX,100 ; next century. (Planning ahead!)
DISPLAY_DATE: XOR CL,CL ;Display leading zeros.
MOV CH,SPACE
CALL STORE_WORD ;Store it.
TIME: INC DI ;Move to time field.
MOV DX,DTA.FILE_TIME ;Retrieve time.
MOV AX,DX
MOV CL,11 ;Shift to hours bits.
SHR AX,CL
PUSH AX
CMP AX,12 ;Past noon?
JBE MERIDIAN
SUB AX,12 ;If yes, adjust.
MERIDIAN: CMP AX,0 ;Midnight?
JNZ NOT_MIDNIGHT
MOV AX,12 ;If yes, adjust.
NOT_MIDNIGHT: MOV CL,0FFH ;Suppress leading zeros.
MOV CH,":"
CALL STORE_WORD ;Store it.
MOV AX,DX ;Retrieve time.
MOV CL,5 ;Shift to minutes bits.
SHR AX,CL
AND AX,111111B ;Mask off all but minutes.
XOR CL,CL
POP DX ;Retrieve hours.
MOV CH,"p" ;Assume PM.
CMP DX,12 ;Is it PM?
JAE PM
MOV CH,"a" ;If no, AM.
PM: CALL STORE_WORD ;Store it.
XOR AL,AL
STOSB
MOV SI,OFFSET RESULTS ;Print out results
CALL PRINT_STRING
MOV SI,OFFSET CR_LF
CALL PRINT_STRING
RET ;Done here.
;-----------------------------------------------------------------------;
; Converts a two byte hex number to decimal followed by delimiter. ;
; INPUT: AX = hex number; BL = 10; CH = delimiter character to store. ;
; CL = 0 if zeros are to be stored; CL = -1 if leading zeros ignored. ;
; ES:DI points to storage. ;
;-----------------------------------------------------------------------;
STORE_WORD: DIV BL ;Divide by ten.
ADD AX,"00" ;Convert to ASCII.
CMP CL,0 ;Are we to display leading zero?
JZ STORE_IT ;If yes, store as is.
CMP AL,"0" ;Is it a leading zero?
JNZ STORE_IT ;If no, store it.
MOV AL,SPACE ;Else, store a space.
STORE_IT: STOSW
MOV AL,CH ;Store delimiter character also.
STOSB
RET
;----------------------------------------------;
; INPUT: DX = first row to write spaces. ;
;----------------------------------------------;
BLANK_LINE: PUSH DX
CALL SET_CURSOR
MOV CX,80 * 4
NEXT_BLANK: MOV AX,0EH SHL 8 + SPACE ;Write spaces via BIOS.
INT 10H
LOOP NEXT_BLANK
POP DX
CALL SET_CURSOR
RET
;----------------------------------------------;
GET_DIR: MOV BYTE PTR [SI],"\" ;DOS doesn't preface directory
INC SI ; with slash so we must.
XOR DL,DL
MOV AH,47H ;Get current directory.
INT 21H
RET
CHANGE_DIR: MOV AH,3BH ;Change current directory.
INT 21H
RET
;----------------------------------------------;
SET_CURSOR: XOR BH,BH ;Video page zero.
MOV AH,2 ;Set cursor position.
INT 10H
RET
GET_CURSOR: XOR BH,BH
MOV AH,3 ;Get cursor position.
INT 10H
RET
;----------------------------------------------;
GET_KEY: XOR AH,AH ;Get a keystroke.
INT 16H
RET
CK_KEY: MOV AH,1 ;ZR = 1 if no keystroke available
INT 16H ;ZR = 0 if keystroke is available
RET
CLEAR_IT: CALL GET_KEY
CLEAR_KEY: CALL CK_KEY ;If any keystrokes available,
JNZ CLEAR_IT ; remove them.
RET
;----------------------------------------------;
WRITE_TTY: MOV AH,0EH ;Write TTY via BIOS.
INT 10H
PRINT_STRING: LODSB
OR AL,AL ;ASCIIZ string.
JNZ WRITE_TTY
RET
CURRENT_DIR = $
WORKING_DIR = CURRENT_DIR + 68
RESULTS = WORKING_DIR + 68
DTA = RESULTS + 41
TREE_LEVEL = DTA + SIZE MATCHING
STACK_PTR = TREE_LEVEL + 100 + 256
ZEROS = STACK_PTR
_TEXT ENDS
END START